package labox.innovation.gameserver;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.sun.org.apache.xml.internal.serialize.LineSeparator;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

import javolution.util.FastList;
import labox.innovation.config.Config;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.network.SystemMessageId;
import labox.innovation.gameserver.network.clientpackets.Say2;
import labox.innovation.gameserver.network.serverpackets.CreatureSay;
import labox.innovation.gameserver.network.serverpackets.SystemMessage;
import labox.innovation.gameserver.script.DateRange;
import labox.innovation.gameserver.util.Broadcast;

/**
 * 这个类处理所有公告
 * 
 * @version $Revision: 1.5.2.1.2.7 $ $Date: 2005/03/29 23:15:14 $
 */
public final class Announcements {

	private static final Logger _log = LoggerFactory.getLogger(Announcements.class.getName());

	private static final List<Announcement> _announcements = new FastList<Announcement>();

	private static class Announcement {
		public DateRange _dr;

		public String _msg;

		public int _type;

		public Announcement(DateRange dr, String msg, int type) {
			_dr = dr;
			_msg = msg;
			_type = type;
		}
	}

	private Announcements() {
		loadAnnouncements();
	}

	public static Announcements getInstance() {
		return SingletonHolder._instance;
	}

	/**
	 * 从本地文件装载公告数据
	 * 
	 */
	public void loadAnnouncements() {
		_announcements.clear();
		File file = new File(Config.DATAPACK_ROOT, "data/announcements.xml");
		if (file.exists()) {
			readFromDisk(file);
		} else {
			_log.info("data/announcements.xml doesn't exist",Config.SERVER_ID,"loadAnnouncements");
		}
	}

	/**
	 * 向某个玩家显示公告
	 * 
	 * @param activeChar
	 */
	public void showAnnouncements(FuzePcInstance activeChar) {
		for (int i = 0; i < _announcements.size(); i++) {
			Announcement ann = _announcements.get(i);
			if (ann._dr.isInvalid() || ann._dr.isWithinRange(GameTimeController.getInstance().getNowDate())) {
				switch (ann._type) {
				case 0:
					CreatureSay cs = new CreatureSay(0, Say2.NOTICE_CHANNALI, activeChar.getName(), ann._msg);
					activeChar.sendPacket(cs);
					break;
				case 1:
					SystemMessage sm = new SystemMessage(SystemMessageId.S1);
					sm.addString(ann._msg);
					activeChar.sendPacket(sm);
					break;
				}
			}
		}
	}

	/**
	 * 向GM列出所有的公告
	 * 
	 * @param activeChar
	 */
	public void listAnnouncements(FuzePcInstance activeChar) {
		//		String content = HtmCache.getInstance().getHtmForce("data/html/admin/announce.htm");
		//		NpcHtmlMessage adminReply = new NpcHtmlMessage(5);
		//		adminReply.setHtml(content);
		//		final StringBuilder replyMSG = StringUtil.startAppend(500, "<br>");
		//		for (int i = 0; i < _announcements.size(); i++) {
		//			StringUtil.append(replyMSG, "<table width=260><tr><td width=220>", _announcements.get(i), "</td><td width=40>" + "<button value=\"Delete\" action=\"bypass -h admin_del_announcement ", String.valueOf(i), "\" width=60 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td></tr></table>");
		//		}
		//		adminReply.replace("%announces%", replyMSG.toString());
		//		activeChar.sendPacket(adminReply);
	}

	/**
	 * 加入一个公告文字
	 * 
	 * @param text
	 */
	public void addAnnouncement(String text) {
		_announcements.add(new Announcement(new DateRange(null, null), text, 0));
		saveToDisk();
	}

	/**
	 * 根据公告索引删除一个公告
	 * 
	 * @param line
	 */
	public void delAnnouncement(int line) {
		_announcements.remove(line);
		saveToDisk();
	}

	private void readFromDisk(File file) {
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(file);
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document document = builder.parse(fin);
			Element root;
			root = document.getDocumentElement();
			if (root.getTagName().compareTo("Announcements") != 0) {
				throw new Exception("Not a Announcement file!");
			}
			NodeList nl = root.getElementsByTagName("Announcement");

			for (int i = -1; ++i < nl.getLength();) {
				Element e = (Element) nl.item(i);
				_announcements.add(new Announcement(DateRange.parse(e.getAttribute("date"), GameTimeController.d), e.getTextContent(), Integer.parseInt(e.getAttribute("type"))));
			}

			if (_log.isDebugEnabled())
				_log.debug("Announcements: Loaded " + nl.getLength() + " Announcements.");
		} catch (Exception e) {
			_log.error("Error reading announcements: ", Config.SERVER_ID, e);
		} finally {
			try {
				if (fin != null)
					fin.close();
			} catch (Exception e2) {
				// nothing
			}
		}
	}

	private void saveToDisk() {
		File file = new File(Config.DATAPACK_ROOT, "data/announcements.xml");
		FileOutputStream fon = null;
		try {
			fon = new FileOutputStream(file);

			Document document = null;
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.newDocument();
			Element e, root = (Element) document.createElement("Announcements");
			document.appendChild(root);

			for (int i = -1; i < _announcements.size();) {
				Announcement ann = _announcements.get(i);
				e = document.createElement("Announcement");
				e.setAttribute("date", ann._dr.toString());
				e.setAttribute("type", String.valueOf(ann._type));
				e.setTextContent(ann._msg);
				root.appendChild(e);
			}

			try {
				OutputFormat outformat = new OutputFormat(document);
				outformat.setLineSeparator(LineSeparator.Windows);
				outformat.setIndenting(true);
				outformat.setIndent(4);
				outformat.setLineWidth(0);
				outformat.setPreserveSpace(false);
				XMLSerializer xmlSerilizer = new XMLSerializer(fon, outformat);
				xmlSerilizer.serialize(document);
			} catch (Exception ex) {
				System.out.println("create xml failed...\n" + ex.getMessage());
			}

		} catch (Exception e) {
			_log.error("Saving to the announcements file has failed: ", Config.SERVER_ID,e);
		} finally {
			try {
				if (fon != null)
					fon.close();
			} catch (Exception e) {
			}
		}
	}

	public void announceToAll(String text) {
		Broadcast.announceToOnlinePlayers(text);
	}

	public void announceToAll(SystemMessage sm) {
		Broadcast.toAllOnlinePlayers(sm);
	}

	public void announceToInstance(SystemMessage sm, int instanceId) {
		Broadcast.toPlayersInInstance(sm, instanceId);
	}

	// Method for handling announcements from admin
	public void handleAnnounce(String command, int lengthToTrim) {
		try {
			// Announce string to everyone on server
			String text = command.substring(lengthToTrim);
			SingletonHolder._instance.announceToAll(text);
		}

		// No body cares!
		catch (StringIndexOutOfBoundsException e) {
			// empty message.. ignore
		}
	}

	@SuppressWarnings("synthetic-access")
	private static class SingletonHolder {
		protected static final Announcements _instance = new Announcements();
	}
}
